float4x4 World;
float4x4 View;
float4x4 Projection;
float2 ViewPortScale;

float size;
float dt;
float3 globalForce;
float attractiveLocalCount;
float3 attractiveLocals[10];
float attractiveLocalMagnitudes[10];
float repulsiveLocalCount;
float3 repulsiveLocals[10];
float repulsiveLocalMagnitudes[10];

float4x4 MatrixTransform : register(vs, c0);

texture ParticleTex;
sampler PartSampler = sampler_state
{
	Texture = <ParticleTex>;
};

texture PositionTex;
sampler PosSampler = sampler_state
{
    Texture = <PositionTex>;
	MipFilter = None;
	MinFilter = Point;
	MagFilter = Point;
	AddressU = Clamp;
	AddressV = Clamp;
};

texture VelocityTex;
sampler VelSampler = sampler_state
{
	Texture = <VelocityTex>;
	MipFilter = None;
	MinFilter = Point;
	MagFilter = Point;
	AddressU = Clamp;
	AddressV = Clamp;
};

struct VertexShaderInput
{
    float2 Corner : TEXCOORD0;
	float2 UV : POSITION0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
	float2 TextureCoordinate : COLOR1;
};

float ComputeParticleSize(float s)
{   
    // Project the size into screen coordinates.
    return s * Projection._m11;
}

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
	
	float4 pos = tex2Dlod(PosSampler, float4(input.UV / 1000, 0, 0));
	pos.w = 1;
    float4 worldPosition = mul(pos, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

	float s = ComputeParticleSize(size);
	output.Position.xy += input.Corner * s * ViewPortScale;
	output.TextureCoordinate = (input.Corner + 1) / 2;

    return output;
}

float4 VelocityPixelShader(in float2 texCoord : TEXCOORD0) : COLOR0
{
	float4 pos = tex2D(PosSampler, texCoord); // PosSampler is s0 (texture passed into spritebatch draw call hurrrrr durrrr)
	float4 velocity = tex2D(VelSampler, texCoord);

	float3 totalForce = globalForce;

	float dist;
	float3 dir;
	[loop]
	for (int i = 0; i < attractiveLocalCount; i++)
	{
		//  
		// F = magnitude / r^2
		//
		dist = distance(pos, attractiveLocals[i]);
		dist = dist < 2000 ? 2000 : dist;
		dist = dist > 4000 ? 4000 : dist;
		dir = normalize(attractiveLocals[i] - pos.xyz);
		totalForce += dir * attractiveLocalMagnitudes[i] / (dist);
	}

	[loop]
	for (int j = 0; j < repulsiveLocalCount; j++)
	{
		//  
		// F =  -magnitude / r^2
		//
		dist = distance(pos, repulsiveLocals[j]);
		dist = dist < 2000 ? 2000 : dist;
		dist = dist > 4000 ? 4000 : dist;
		dir = normalize(pos.xyz - repulsiveLocals[i]);
		totalForce += dir * repulsiveLocalMagnitudes[j] / (dist);
	}

	float3 newvel = velocity.xyz + totalForce * dt;

	float3 newpos = pos + newvel * dt;
	float3 N = {0, 1, 0};

	[branch]
	if (newpos.y < 0)
	{
		// R = 2*(V dot N)*N - V
		newvel = 0.001 * -2 * dot(newvel, N) * N - newvel;
		newvel.xz *= -1;
	}

	return float4(newvel, 0);
}

float4 PositionPixelShader(in float2 texCoord : TEXCOORD0, float4 color : COLOR0) : COLOR0
{
	float4 pos = tex2D(PosSampler, texCoord);
	float4 velocity = tex2D(VelSampler, texCoord);
	float3 newpos = pos.xyz + velocity.xyz * dt;
	return float4(newpos,0);
}

void SpriteVertexShader(inout float4 color    : COLOR0,
                        inout float2 texCoord : TEXCOORD0,
                        inout float4 pos : POSITION0)
{
	pos = mul(pos, MatrixTransform);
}

float4 PixelShaderFunction(VertexShaderOutput input, in float4 color : COLOR0) : COLOR0
{
	float4 finalColor = tex2D(PartSampler, input.TextureCoordinate);
	return finalColor;
}

technique Render
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }
}

technique Velocity
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 SpriteVertexShader();
		PixelShader = compile ps_3_0 VelocityPixelShader();
	}
}

technique Position
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 SpriteVertexShader();
		PixelShader = compile ps_3_0 PositionPixelShader();
	}
}
